// buildValidator.ts
import { validatorPool } from '@/utils/validatorPool';


/**
 * 验证中国大陆 18 位身份证号码的校验码
 * 符合 GB 11643-1999 标准（ISO 7064 : 1983 , MOD 11-2）
 */
export function checkIdCardAlgo(id: string): boolean {
  // 1. 长度 & 基本字符
  if (!/^\d{17}[\dXx]$/.test(id)) return false;

  // 2. 加权因子
  const weights = [7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2];
  // 3. 校验码映射
  const checkMap = ['1', '0', 'X', '9', '8', '7', '6', '5', '4', '3', '2'];

  // 4. 计算加权和
  let sum = 0;
  for (let i = 0; i < 17; i++) {
    sum += parseInt(id[i], 10) * weights[i];
  }

  // 5. 模 11 得到索引
  const mod = sum % 11;
  const expectChar = checkMap[mod]; // 计算出的校验码

  // 6. 与第 18 位比对（忽略大小写）
  const actualChar = id[17].toUpperCase();
  return actualChar === expectChar;
}


/**
 * Builds a form field validator function from a rule descriptor.
 *
 * @param ruleObj - Validation rule object. Supported properties include:
 *   - fieldName?: string — used in default error messages.
 *   - required?: boolean — treats null/undefined/'' as empty and fails if empty.
 *   - numeric?: boolean — treat value as Number(value) and perform numeric checks.
 *   - min?: number, max?: number — legacy bounds used for length (unless numeric) or numeric bounds when numeric is true (kept for backward compatibility).
 *   - exactLength?: number, minLength?: number, maxLength?: number — explicit length constraints (take precedence over min/max).
 *   - minValue?: number, maxValue?: number — preferred numeric bounds.
 *   - pattern?: RegExp | string — custom regex to test trimmed string; string forms are passed to new RegExp(...).
 *   - patternTip?: string — custom message when pattern fails.
 *   - email?: boolean, emailTip?: string — basic email format check.
 *   - phone?: boolean, phoneTip?: string — basic mobile phone regex check.
 *   - idCard?: boolean, idCardTip?: string — regex + checkIdCardAlgo() for 18-digit ID validation.
 *   - integer?: boolean, integerTip?: string — integer-only check.
 *   - enum?: any[] | in?: any[], enumTip?: string — allowed values (checks original value, not trimmed).
 *   - alpha?: boolean, alphaTip?: string — letters only.
 *   - alphaNum?: boolean, alphaNumTip?: string — letters and digits only.
 *   - noWhitespace?: boolean, noWhitespaceTip?: string — disallow whitespace.
 *   - money?: boolean, moneyTip?: string — numeric with up to two decimal places.
 *   - url?: boolean, urlTip?: string — validated via the URL constructor.
 *   - custFunction?: string — name/key of a custom validator in validatorPool; may be synchronous, callback-based, or return a Promise.
 *
 * @returns A validator function with signature (rule, value, cb) => void that:
 *   - trims the value (null/undefined → ''), runs the configured checks in order,
 *   - calls cb() on success, or cb(Error) with an appropriate message on failure,
 *   - for custFunction: looks up validatorPool[custFunction], throws if missing, supports callback usage or Promise-returning functions (Promise resolution calls cb(), rejection passes Error to cb()).
 *
 * @remarks
 *   - Pattern and specific validators prefer developer-supplied "*Tip" messages; otherwise sensible defaults are used.
 *   - enum/in validation uses Array.includes on the original (untrimmed vs. trimmed) value as implemented.
 *   - Numeric conversion failures produce a "must be a number" error.
 *   - Any thrown value from custom validators is coerced to Error before being passed to cb.
 *
 * @example
 * // Example usage in a form rule:
 * // { validator: buildValidator({ fieldName: '年龄', numeric: true, minValue: 0, maxValue: 120 }) }
 */
export function buildValidator(ruleObj: any) {
  return (_: any, value: any, cb: Function) => {
    const val = value === null || value === undefined ? '' : String(value).trim();
 
    // 1. 必填
    if (ruleObj.required && !val) {
      return cb(new Error(`${ruleObj.fieldName}不能为空`));
    }
    if (!ruleObj.required && !val) return cb();



    // 3. 通用规则
    const len = val.length;
    // 兼容旧的 min/max（既可表示长度，也可用于 numeric）
    if (ruleObj.min !== undefined && !ruleObj.numeric && len < ruleObj.min) {
      return cb(new Error(`长度不能小于 ${ruleObj.min}`));
    }
    if (ruleObj.max !== undefined && !ruleObj.numeric && len > ruleObj.max) {
      return cb(new Error(`长度不能超过 ${ruleObj.max}`));
    }

    // 明确的长度控制（优先于旧的 min/max）
    if (ruleObj.exactLength !== undefined && len !== ruleObj.exactLength) {
      return cb(new Error(`长度必须为 ${ruleObj.exactLength}`));
    }
    if (ruleObj.minLength !== undefined && len < ruleObj.minLength) {
      return cb(new Error(`长度不能小于 ${ruleObj.minLength}`));
    }
    if (ruleObj.maxLength !== undefined && len > ruleObj.maxLength) {
      return cb(new Error(`长度不能超过 ${ruleObj.maxLength}`));
    }

    // 数值相关边界（推荐使用 minValue/maxValue; 保留 numeric + min/max 向后兼容）
    if (ruleObj.numeric) {
      const n = Number(val);
      if (isNaN(n)) return cb(new Error('必须是数字'));
      if (ruleObj.minValue !== undefined && n < ruleObj.minValue) return cb(new Error(`不能小于 ${ruleObj.minValue}`));
      if (ruleObj.maxValue !== undefined && n > ruleObj.maxValue) return cb(new Error(`不能大于 ${ruleObj.maxValue}`));
      if (ruleObj.min !== undefined && n < ruleObj.min) return cb(new Error(`不能小于 ${ruleObj.min}`));
      if (ruleObj.max !== undefined && n > ruleObj.max) return cb(new Error(`不能大于 ${ruleObj.max}`));
    }

     /* ----------  pattern 校验 ---------- */
if (ruleObj.pattern) {
  const reg = typeof ruleObj.pattern === 'string'
    ? new RegExp(ruleObj.pattern)
    : ruleObj.pattern;

  if (!reg.test(val)) {
    // 1. 优先使用开发者给出的提示
    const msg = ruleObj.patternTip
      ? ruleObj.patternTip
      // 2. 也可以在这里继续扩展「常见正则 -> 文案」映射
      : '格式不正确';

    return cb(new Error(msg));
  }
}

/* ----------  email 校验 ---------- */
if (ruleObj.email) {
  // 通用邮箱正则，可按业务再调严或放宽
  const emailReg = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
  if (!emailReg.test(val)) {
    const msg = ruleObj.emailTip || '请输入正确的邮箱地址';
    return cb(new Error(msg));
  }
}

/* ----------  phone 校验 ---------- */
if (ruleObj.phone) {
  const phoneReg = /^1[3-9]\d{9}$/;
  if (!phoneReg.test(val)) {
    return cb(new Error(ruleObj.phoneTip || '请输入11位有效手机号'));
  }
}

/* ----------  idCard 校验 ---------- */
if (ruleObj.idCard) {
  const idReg = /^[1-9]\d{5}(18|19|20)\d{2}(0[1-9]|1[0-2])(0[1-9]|[12]\d|3[01])\d{3}[\dXx]$/;
  if (!idReg.test(val) || !checkIdCardAlgo(val)) { // 加权算法自行实现
    return cb(new Error(ruleObj.idCardTip || '请输入18位有效身份证号'));
  }
}

/* ----------  integer 校验 ---------- */
if (ruleObj.integer && !/^-?\d+$/.test(val)) {
  return cb(new Error(ruleObj.integerTip || '必须是整数'));
}

/* ----------  enum / in 校验（枚举） ---------- */
if (ruleObj.enum || ruleObj.in) {
  const arr = ruleObj.enum || ruleObj.in;
  if (!Array.isArray(arr) || !arr.includes(value)) {
    return cb(new Error(ruleObj.enumTip || `值必须为以下之一: ${JSON.stringify(arr)}`));
  }
}

/* ----------  alpha / alphaNum / noWhitespace ---------- */
if (ruleObj.alpha && !/^[A-Za-z]+$/.test(val)) {
  return cb(new Error(ruleObj.alphaTip || '只能包含字母'));
}
if (ruleObj.alphaNum && !/^[A-Za-z0-9]+$/.test(val)) {
  return cb(new Error(ruleObj.alphaNumTip || '只能包含字母和数字'));
}
if (ruleObj.noWhitespace && /\s/.test(val)) {
  return cb(new Error(ruleObj.noWhitespaceTip || '不能包含空白字符'));
}

/* ----------  money 校验 ---------- */
if (ruleObj.money && !/^-?\d+(\.\d{1,2})?$/.test(val)) {
  return cb(new Error(ruleObj.moneyTip || '金额最多保留两位小数'));
}

/* ----------  url 校验 ---------- */
if (ruleObj.url) {
  try { new URL(val); } catch {
    return cb(new Error(ruleObj.urlTip || '请输入有效网址'));
  }
}

    // 2. 自定义函数（白名单），支持同步或返回 Promise 的异步函数
    if (ruleObj.custFunction) {
      const fn = validatorPool[ruleObj.custFunction];

      if (!fn) return cb(new Error(`未知函数 ${ruleObj.custFunction}`));

      try {
        const res = fn(_, val, cb);
        if (res && typeof res.then === 'function') {
          // 返回 Promise 的情况：在 then/catch 中处理 cb
          return res.then(() => cb()).catch((err: any) => cb(err instanceof Error ? err : new Error(String(err))));
        }
        // 若函数使用回调模式（直接调用 cb）或同步返回，则结束校验
        return; 
      } catch (e: any) {
        return cb(e instanceof Error ? e : new Error(String(e)));
      }
    }
    cb();
  };
}
